package com.xuecheng.content.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xuecheng.basic.api.BasicClient;
import com.xuecheng.commons.constants.CourseConstants;
import com.xuecheng.commons.enums.ErrorCode;
import com.xuecheng.commons.model.vo.CategoryVo;
import com.xuecheng.commons.model.vo.ResponseResult;
import com.xuecheng.commons.model.vo.TeachplanVo;
import com.xuecheng.commons.utils.BeanHelper;
import com.xuecheng.content.config.MinioProperties;
import com.xuecheng.content.domain.CourseBase;
import com.xuecheng.content.domain.CoursePub;
import com.xuecheng.content.mappers.CoursePubMapper;
import com.xuecheng.content.service.CourseBaseService;
import com.xuecheng.content.service.CoursePubService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.content.service.TeachplanService;
import com.xuecheng.web.exception.BusinessException;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.errors.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 课程发布 服务实现类
 * </p>
 *
 * @author itheima
 * @since 2022-07-16
 */
@Service
@Slf4j
public class CoursePubServiceImpl extends ServiceImpl<CoursePubMapper, CoursePub> implements CoursePubService {
        /**
         *  * 获取如下信息
         *  *       1、课程信息
         *  *       2、课程老师
         *  *       3、课程计划信息
         */
        @Autowired
        private TeachplanService teachplanService;
        @Autowired
        private CourseBaseService courseBaseService;
        @Autowired
        private BasicClient basicClient;
        @Override
        public Map preview(Long courseBaseId) {
                // 判断参数
                if(courseBaseId == null){
                        throw new BusinessException(ErrorCode.ERROR);
                }
                // 查询 课程计划 信息
                ResponseResult<TeachplanVo> treeNodes = teachplanService.treeNodes(courseBaseId);
                TeachplanVo teachplanVo = treeNodes.getData();
                // 查询课程基本信息
                CourseBase courseBase = courseBaseService.getById(courseBaseId);
                // coursePub的大部分属性和CourseBase属性一致
                CoursePub coursePub = BeanHelper.copyProperties(courseBase, CoursePub.class);
                // 设置数据
                coursePub.setCourseId(courseBase.getId());
                // 获取大分类名称 --- 分类需要远程调用basic微服务
                coursePub.setMt(getCategoryName(coursePub.getMt()));
                // 获取小分类名称
                coursePub.setSt(getCategoryName(coursePub.getSt()));
                // 设置课程计划的json
                coursePub.setTeachplan(JSON.toJSONString(teachplanVo));
                // 构造返回值
                Map map = new HashMap();
                map.put("teachplanNode",teachplanVo);
                map.put("coursePub", coursePub);
                return map;
        }

        //根据id，调用feign接口查询分类名称
        public String getCategoryName(String id) {
                ResponseResult<CategoryVo> result = basicClient.findById(id);
                CategoryVo categoryVo = result.getData();
                return categoryVo.getName();
        }

        @Autowired
        private PublishHtmlService publishHtmlService;
        @Autowired
        private RabbitTemplate rabbitTemplate;
        /**
         * 1. 保存课程发布数据到mysql
         * 2.
         * @param courseBaseId 课程id
         * @return vo
         */
        @Override
        @Transactional
        public ResponseResult publish(Long courseBaseId) throws Exception {
                //A --保存发布数据到数据库--
                //1 参数判断，课程id 是否为空 课程是否存在 课程状态是否合适（审核未通过 不可以发布）
                if(courseBaseId == null){
                        throw new BusinessException(ErrorCode.PARAMSERROR);
                }
                CourseBase course = courseBaseService.getById(courseBaseId);
                if(course == null){
                        throw new BusinessException(ErrorCode.NOTFOUND);
                }
                if (!CourseConstants.AUDIT_PASTED_STATUS.equals(course.getAuditStatus())) {
                        throw new BusinessException(ErrorCode.MEDIABINDERROR);
                }
                //2 从预览获取课程发布对象
                Map preview = this.preview(courseBaseId);
                CoursePub coursePub = (CoursePub) preview.get("coursePub");
                // 清空合并对象的id值 防止污染
                coursePub.setId(null);
                //3 根据课程id 查询当前课程是否有发布记录
                LambdaQueryWrapper<CoursePub> qw = new LambdaQueryWrapper<>();
                qw.eq(CoursePub::getCourseId,courseBaseId);
                CoursePub pub = this.getOne(qw);
                // 不存在 添加
                if(ObjectUtil.isEmpty(pub)){
                        this.save(coursePub);
                }else { //存在 修改
                        coursePub.setId(pub.getId());
                        this.updateById(coursePub);
                }
                //4 修改课程状态
                course.setAuditStatus(CourseConstants.AUDIT_PUBLISHED_STATUS);
                courseBaseService.updateById(course);
                log.info("保存课程发布数据线程 = " + Thread.currentThread().getName());

                //B --生成html页面 存入minio
                publishHtmlService.publishHtml(preview, coursePub.getId());

                //C --发送异步Mq消息(交换机 路由键 消息对象)
                rabbitTemplate.convertAndSend("content-exchange","course.pub",coursePub.getId());
                return ResponseResult.okResult();
        }

        /**
         * 课程下架后 逻辑删除发布表中的课程
         */
        public ResponseResult deleteById(Long courseId){
                // 获取课程发布对象
                //这里不对，courseId是课程的id，这里要查询的时候课程发布数据,也就是coursePub表。
                //是不是应该根据course_id字段查询出发布数据呢？***
                LambdaQueryWrapper<CoursePub> qw = new LambdaQueryWrapper<>();
                qw.eq(CoursePub::getCourseId,courseId);
                CoursePub coursePub = this.getOne(qw);
                if(coursePub == null){
                        throw new BusinessException(ErrorCode.PARAMSERROR);
                }
                // mysql中逻辑删除已经上传的指定课程
                boolean remove = this.removeById(coursePub.getId());
                if(!remove){
                    throw new BusinessException(ErrorCode.DELETEERROR);
                }
                // 删除存入Minio 的对应静态页面
                publishHtmlService.deleteHtml(coursePub.getId());
                //C --发送异步Mq消息(交换机 路由键 消息对象)
                rabbitTemplate.convertAndSend("content-exchange","course.offline",coursePub.getId());
                return ResponseResult.okResult();
        }
}
